package othello.gui;

import othello.util.GameDetails;

import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.Stack;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.Queue;
import java.util.List;
import java.util.Set;
import java.util.LinkedList;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

import othello.model.GameBoard;
import othello.model.SimpleGameBoard;
import othello.model.IBoard;
import othello.model.Action;
import othello.model.TrainingMovesFactory;
import othello.players.HumanPlayer;
import othello.players.IPlayer;
import othello.players.learnplayers.AbstractNeuralNetworkPlayer;

public class Othello extends JFrame implements SettingsObserver {
	private static final long serialVersionUID = 1L;
    private static Queue<GameDetails> trainingSettings = new LinkedList<GameDetails>();    
    private static boolean drawBoard = false;
	private ExecutorService threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    private JPanel gsp = new GeneralSettingsPanel();
    private JPanel tsp = new TrainingSettingsPanel(this);
    private ResultsPanel rp = new ResultsPanel();
    private GamePanel gp = new GamePanel();
    
    private static List<byte[][]> startingPositionsWhite;
    private static List<byte[][]> startingPositionsBlack;

    public Othello(String title) throws HeadlessException {
        super(title);
        // Set windows size
        setSize(800, 450);
        // Close application on close button
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Init menubar and content
        init();
        // Center window
        setLocationRelativeTo(getRootPane());
        // Add general settings panel by default
        add(tsp);
    }

    public static void addS(GameDetails g) {
		//~ if(trainingSettings != null) {
            //~ trainingSettings.push(g);
        //~ } else {
            //~ trainingSettings = new Stack<GameDetails>();
            //~ trainingSettings.push(g);
        //~ }
        trainingSettings.add(g);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                Othello o = new Othello("FEEM Othello Machine Learning");
                o.setVisible(true);
            }
        });
    }

    private void init() {
        //Where the GUI is created:
        JMenuBar menuBar;
        JMenu menu;
        JMenuItem menuItem;

        //Create the menu bar.
        menuBar = new JMenuBar();

        //Build the first menu.
        menu = new JMenu("Training");
        menuBar.add(menu);

        //a group of JMenuItems
        menuItem = new JMenuItem("Training Settings");
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
        menuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                getContentPane().removeAll();
                add(tsp);
                validate();
                repaint();
            }
        });
        menu.add(menuItem);

        //Build the second menu.
        menu = new JMenu("General");
        menuBar.add(menu);

        //a group of JMenuItems
        menuItem = new JMenuItem("General Settings");
        menuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_5, ActionEvent.ALT_MASK));
        menuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                getContentPane().removeAll();
                add(gsp);
                validate();
            }
        });
        menu.add(menuItem);

        setJMenuBar(menuBar);
    }    
    
    
    @Override
    public void startTraining() {
        getContentPane().removeAll();        

        JPanel buttonPane = new JPanel();
        buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.PAGE_AXIS));
        buttonPane.setBorder(BorderFactory.createEmptyBorder(0, 10, 10, 10));
        buttonPane.add(Box.createHorizontalGlue());
        buttonPane.add(rp);
        buttonPane.add(gp);

        add(buttonPane);
        
        startTournament();
		
        validate();
        repaint();
    }

    static int counter = 0;
    
    private void startTournament() {
    	
        // starts a thread or uses one of the existing threads or waits until a thread becomes available.
        threadPool.execute(
            new Runnable() {
                @Override
                public void run() {
                	// Within this thread                    
                    GameDetails s = trainingSettings.remove();                    
                    s.addObserver(rp);
                    s.addObserver(gp);
                    
                    // Create game board
                    IBoard board = new GameBoard(8, 8, s, startingPositionsWhite, startingPositionsBlack);
                    
                    // Set gui
                    gp.setBoard(board);
                    gp.setDrawBoard(drawBoard);
                    
                    for (IPlayer player : board.getPlayersList()) {                        
                    	if (player instanceof HumanPlayer) {
                    		gp.setDrawBoard(true);
                    	} else if (player instanceof AbstractNeuralNetworkPlayer) {
                    		AbstractNeuralNetworkPlayer nn_player = (AbstractNeuralNetworkPlayer) player;
                    		nn_player.setNNLearningRate(s.getNNLearningRate());
                    		nn_player.setLoadWeights(s.getLoadGames());
                    		nn_player.setSaveWeights(s.getSaveGames());
                    		nn_player.setUseEligibility(s.getUseEligibility());
                    		nn_player.loadWeights();
                    	}
                    }
                    
                    // Start game
                    board.beginTournament();
                }
            }
        );
    }

    public static void setDrawBoard(boolean enabled) {
        drawBoard = enabled;
    }
    
    public static void setTrainingMoves(GameDetails g) {
		TrainingMovesFactory trainFact = new TrainingMovesFactory(g);
		startingPositionsWhite = trainFact.getStartingPositionsWhite();
		startingPositionsBlack = trainFact.getStartingPositionsGray();		
	}
}
